package com.developer.platform.infrastructure.config;

import com.developer.platform.domain.service.IAuthenticationSource;
import com.developer.platform.domain.service.IAuthorizationSource;
import com.developer.platform.domain.service.IAuthorizingRealm;
import com.developer.platform.infrastructure.error.SecurityErrorType;
import com.developer.platform.infrastructure.security.AuthenticationFilter;
import com.developer.platform.infrastructure.security.SecurityAccessDecisionManager;
import com.developer.platform.infrastructure.security.SecurityAuthenticationProvider;
import com.developer.platform.infrastructure.security.SecurityMetadataSource;
import com.developer.platform.infrastructure.security.handler.ErrorHandler;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.veeker.core.exceptions.BusinessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * 安全配置类
 *
 * @author ：qiaoliang
 * @date ：2020-10-14
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private  IAuthorizationSource authorizationResource;
    @Autowired
    private  IAuthenticationSource authenticationSource;
    @Autowired
    private  IAuthorizingRealm authorizingRealm;
    @Autowired
    private SecurityAccessDecisionManager securityAccessDecisionManager;
    @Autowired
    private SecurityMetadataSource securityMetadataSource;

    @Override
    public void configure(AuthenticationManagerBuilder auth) {
        // 使用自定义登录身份认证组件
        auth.authenticationProvider(authenticationProvider());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()
                .authorizeRequests()
                // 跨域预检请求
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setSecurityMetadataSource(securityMetadataSource);
                        o.setAccessDecisionManager(securityAccessDecisionManager);
                        return o;
                    }
                })
                .and().exceptionHandling().accessDeniedHandler((httpServletRequest, httpServletResponse, e) ->
                ErrorHandler.forward(httpServletRequest,httpServletResponse,
                        new BusinessException(SecurityErrorType.PERMISSION_DENIED_ERROR)))

                .authenticationEntryPoint((httpServletRequest, httpServletResponse, e) ->
                        ErrorHandler.forward(httpServletRequest,httpServletResponse,
                                new BusinessException(SecurityErrorType.NO_ACCESS_ERROR)));
        //开启模拟请求，比如API POST测试工具的测试，不开启时，API POST为报403错误
        http.csrf().disable();
        // 把默认的UsernamePasswordAuthenticationFilter 过滤器替换成自定义过滤器loginFilter
        http.addFilterAfter(new AuthenticationFilter(authenticationManager(), authorizationResource, authenticationSource),
                UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        return new SecurityAuthenticationProvider(authorizingRealm);
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Bean("jackson2ObjectMapperBuilderCustomizer")
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return jacksonObjectMapperBuilder ->
                jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance)
                        .serializerByType(Long.TYPE, ToStringSerializer.instance);
    }

}
